#!/usr/bin/env python3
"""
优化的 Git 执行器
实现重试机制、批处理和性能监控
"""

import subprocess
import os
import time
import asyncio
import aiofiles
import aiofiles.os
from typing import Optional, Tuple, List, Dict, Any
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
from models.git_repository import GitRepository


@dataclass
class ExecutionResult:
    """执行结果数据类"""
    success: bool
    stdout: str
    stderr: str
    execution_time: float
    retries: int
    repo_path: str


class OptimizedGitExecutor:
    """优化的 Git 执行器"""
    
    def __init__(self, 
                 timeout: int = 300,
                 verbose: bool = False,
                 max_retries: int = 3,
                 retry_delay: float = 1.0,
                 max_workers: int = 4,
                 batch_size: int = 10):
        """
        初始化优化的 Git 执行器
        
        Args:
            timeout: 命令执行超时时间（秒）
            verbose: 是否启用详细输出
            max_retries: 最大重试次数
            retry_delay: 重试延迟（秒）
            max_workers: 最大工作线程数
            batch_size: 批处理大小
        """
        self.timeout = timeout
        self.verbose = verbose
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.max_workers = max_workers
        self.batch_size = batch_size
        
        # 性能统计
        self.stats = {
            'total_commands': 0,
            'successful_commands': 0,
            'failed_commands': 0,
            'total_retries': 0,
            'total_time': 0.0,
            'average_time': 0.0
        }
        
        # 命令缓存
        self._command_cache = {}
    
    def pull_repository(self, repo_path: str, remote: str = "origin", branch: str = "main") -> ExecutionResult:
        """
        执行 git pull 操作（带重试机制）
        
        Args:
            repo_path: 仓库路径
            remote: 远程仓库名称
            branch: 分支名称
            
        Returns:
            ExecutionResult: 执行结果
        """
        return self._execute_with_retry(
            repo_path=repo_path,
            command=["pull", remote, branch],
            operation_name="pull"
        )
    
    def batch_pull_repositories(self, repo_paths: List[str]) -> List[ExecutionResult]:
        """
        批量执行 git pull 操作
        
        Args:
            repo_paths: 仓库路径列表
            
        Returns:
            List[ExecutionResult]: 执行结果列表
        """
        results = []
        
        # 分批处理
        for i in range(0, len(repo_paths), self.batch_size):
            batch = repo_paths[i:i + self.batch_size]
            
            if self.verbose:
                print(f"处理批次 {i//self.batch_size + 1}/{(len(repo_paths) + self.batch_size - 1)//self.batch_size}")
            
            # 并行处理当前批次
            batch_results = self._parallel_execute_batch(batch)
            results.extend(batch_results)
        
        return results
    
    def _parallel_execute_batch(self, repo_paths: List[str]) -> List[ExecutionResult]:
        """
        并行执行批处理
        
        Args:
            repo_paths: 仓库路径列表
            
        Returns:
            List[ExecutionResult]: 执行结果列表
        """
        results = []
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_repo = {
                executor.submit(self.pull_repository, repo_path): repo_path
                for repo_path in repo_paths
            }
            
            for future in as_completed(future_to_repo):
                repo_path = future_to_repo[future]
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    # 创建失败结果
                    result = ExecutionResult(
                        success=False,
                        stdout="",
                        stderr=str(e),
                        execution_time=0.0,
                        retries=0,
                        repo_path=repo_path
                    )
                    results.append(result)
        
        return results
    
    def _execute_with_retry(self, 
                          repo_path: str, 
                          command: List[str], 
                          operation_name: str) -> ExecutionResult:
        """
        带重试机制的命令执行
        
        Args:
            repo_path: 仓库路径
            command: Git 命令
            operation_name: 操作名称
            
        Returns:
            ExecutionResult: 执行结果
        """
        start_time = time.time()
        retries = 0
        last_error = ""
        
        for attempt in range(self.max_retries + 1):
            try:
                # 执行命令
                success, stdout, stderr = self._execute_git_command_safe(repo_path, command)
                
                if success:
                    # 更新统计
                    execution_time = time.time() - start_time
                    self._update_stats(True, execution_time, retries)
                    
                    return ExecutionResult(
                        success=True,
                        stdout=stdout,
                        stderr=stderr,
                        execution_time=execution_time,
                        retries=retries,
                        repo_path=repo_path
                    )
                else:
                    last_error = stderr
                    # 如果是网络错误，等待后重试
                    if self._is_network_error(stderr) and attempt < self.max_retries:
                        wait_time = self.retry_delay * (2 ** attempt)  # 指数退避
                        if self.verbose:
                            print(f"网络错误，{wait_time}秒后重试... (尝试 {attempt + 1}/{self.max_retries})")
                        time.sleep(wait_time)
                        retries += 1
                        continue
                    else:
                        break
                        
            except subprocess.TimeoutExpired:
                last_error = f"命令执行超时（{self.timeout}秒）"
                if attempt < self.max_retries:
                    wait_time = self.retry_delay * (2 ** attempt)
                    if self.verbose:
                        print(f"超时，{wait_time}秒后重试... (尝试 {attempt + 1}/{self.max_retries})")
                    time.sleep(wait_time)
                    retries += 1
                else:
                    break
                    
            except Exception as e:
                last_error = str(e)
                break
        
        # 所有重试都失败
        execution_time = time.time() - start_time
        self._update_stats(False, execution_time, retries)
        
        return ExecutionResult(
            success=False,
            stdout="",
            stderr=last_error,
            execution_time=execution_time,
            retries=retries,
            repo_path=repo_path
        )
    
    def _execute_git_command_safe(self, repo_path: str, command: List[str]) -> Tuple[bool, str, str]:
        """
        安全执行 Git 命令
        
        Args:
            repo_path: 仓库路径
            command: Git 命令列表
            
        Returns:
            Tuple[bool, str, str]: (成功状态, 标准输出, 错误输出)
        """
        try:
            # 验证仓库路径
            if not self._validate_repo_path(repo_path):
                return False, "", f"无效的仓库路径: {repo_path}"
            
            # 安全检查
            if not self._is_safe_git_command(command):
                return False, "", f"不安全的 Git 命令: {command}"
            
            # 构建完整命令
            full_command = ["git"] + command
            
            if self.verbose:
                print(f"执行命令: {' '.join(full_command)} in {repo_path}")
            
            # 执行命令
            result = subprocess.run(
                full_command,
                cwd=repo_path,
                capture_output=True,
                text=True,
                timeout=self.timeout,
                preexec_fn=lambda: os.setsid()
            )
            
            # 检查结果
            if result.returncode == 0:
                return True, result.stdout, ""
            else:
                error_msg = result.stderr or result.stdout
                return False, "", error_msg
                
        except Exception as e:
            return False, "", str(e)
    
    def get_repository_status(self, repo_path: str) -> ExecutionResult:
        """
        获取仓库状态
        
        Args:
            repo_path: 仓库路径
            
        Returns:
            ExecutionResult: 执行结果
        """
        return self._execute_with_retry(
            repo_path=repo_path,
            command=["status", "--porcelain"],
            operation_name="status"
        )
    
    def batch_get_status(self, repo_paths: List[str]) -> List[ExecutionResult]:
        """
        批量获取仓库状态
        
        Args:
            repo_paths: 仓库路径列表
            
        Returns:
            List[ExecutionResult]: 执行结果列表
        """
        results = []
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_repo = {
                executor.submit(self.get_repository_status, repo_path): repo_path
                for repo_path in repo_paths
            }
            
            for future in as_completed(future_to_repo):
                repo_path = future_to_repo[future]
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    result = ExecutionResult(
                        success=False,
                        stdout="",
                        stderr=str(e),
                        execution_time=0.0,
                        retries=0,
                        repo_path=repo_path
                    )
                    results.append(result)
        
        return results
    
    def _validate_repo_path(self, repo_path: str) -> bool:
        """
        验证仓库路径的有效性
        
        Args:
            repo_path: 仓库路径
            
        Returns:
            bool: 路径是否有效
        """
        try:
            path = Path(repo_path)
            
            # 检查路径是否存在
            if not path.exists():
                return False
            
            # 检查是否为目录
            if not path.is_dir():
                return False
            
            # 检查是否为 Git 仓库
            git_dir = path / ".git"
            if not git_dir.exists():
                return False
            
            # 安全检查
            real_path = os.path.realpath(repo_path)
            current_path = os.path.realpath(os.getcwd())
            if not real_path.startswith(current_path):
                return False
            
            return True
            
        except Exception:
            return False
    
    def _is_safe_git_command(self, command: List[str]) -> bool:
        """
        检查 Git 命令是否安全
        
        Args:
            command: Git 命令列表
            
        Returns:
            bool: 命令是否安全
        """
        dangerous_commands = {
            "rm", "mv", "del", "delete",
            "reset", "clean", "fsck",
            "push", "remote", "branch",
            "config", "clone", "init"
        }
        
        # 检查是否包含危险命令
        if command and command[0] in dangerous_commands:
            return False
        
        # 检查是否包含重定向操作
        dangerous_chars = {'>', '<', '|', '&', ';', '`', '$(', '${'}
        for cmd in command:
            if any(char in cmd for char in dangerous_chars):
                return False
        
        return True
    
    def _is_network_error(self, error_msg: str) -> bool:
        """
        判断是否为网络错误
        
        Args:
            error_msg: 错误消息
            
        Returns:
            bool: 是否为网络错误
        """
        network_keywords = [
            "network", "connection", "timeout", "unreachable",
            "failed to connect", "connection refused", "no route"
        ]
        
        return any(keyword.lower() in error_msg.lower() for keyword in network_keywords)
    
    def _update_stats(self, success: bool, execution_time: float, retries: int):
        """
        更新性能统计
        
        Args:
            success: 是否成功
            execution_time: 执行时间
            retries: 重试次数
        """
        self.stats['total_commands'] += 1
        self.stats['total_time'] += execution_time
        self.stats['average_time'] = self.stats['total_time'] / self.stats['total_commands']
        self.stats['total_retries'] += retries
        
        if success:
            self.stats['successful_commands'] += 1
        else:
            self.stats['failed_commands'] += 1
    
    def get_stats(self) -> Dict[str, Any]:
        """
        获取性能统计信息
        
        Returns:
            Dict[str, Any]: 统计信息
        """
        return self.stats.copy()
    
    def clear_stats(self):
        """清空统计信息"""
        self.stats = {
            'total_commands': 0,
            'successful_commands': 0,
            'failed_commands': 0,
            'total_retries': 0,
            'total_time': 0.0,
            'average_time': 0.0
        }


def test_optimized_executor():
    """测试优化的 Git 执行器"""
    print("测试优化的 Git 执行器...")
    
    executor = OptimizedGitExecutor(
        timeout=30,
        verbose=True,
        max_retries=2,
        max_workers=4,
        batch_size=2
    )
    
    # 测试单个仓库
    current_dir = "."
    if os.path.exists(os.path.join(current_dir, ".git")):
        print("测试单个仓库 pull...")
        result = executor.pull_repository(current_dir)
        print(f"结果: 成功={result.success}, 用时={result.execution_time:.2f}秒, 重试={result.retries}")
    
    # 测试批处理
    print("\n测试批处理...")
    test_repos = [".", ".."]  # 测试仓库列表
    results = executor.batch_pull_repositories(test_repos)
    
    print("批处理结果:")
    for result in results:
        print(f"  {result.repo_path}: 成功={result.success}, 用时={result.execution_time:.2f}秒")
    
    # 打印统计信息
    stats = executor.get_stats()
    print("\n性能统计:")
    for key, value in stats.items():
        print(f"  {key}: {value}")


if __name__ == "__main__":
    test_optimized_executor()